#pragma once

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <string>

#include "log.hpp"

enum{
    SocketError = 2,
    BindError,
    ListenError,
};

class Sock
{
public:
    Sock()
    {}
    ~Sock()
    {

    }
public:
    void Socket()
    {
        sockfp_ = socket(AF_INET, SOCK_STREAM, 0);
        if(sockfp_ < 0)
        {
            lg(Fatal, "SocketError: %s, errno: %d", strerror(errno), errno);
            exit(SocketError);
        }
    }

    void Bind(uint16_t port)
    {
        struct sockaddr_in server;
        socklen_t len = sizeof(server);
        memset(&server, 0, len);

        server.sin_family = AF_INET;
        server.sin_port= htons(port);
        server.sin_addr.s_addr = INADDR_ANY; // 使用0.0.0.0ip地址
        if(bind(sockfp_, (const struct sockaddr*)&server, len) < 0)
        {
            lg(Fatal, "BindError: %s, errno: %d", strerror(errno), errno);
            exit(BindError);
        }
    }

    void Listen()
    {
        if(listen(sockfp_, 5) < 0)
        {
            lg(Fatal, "ListenError: %s, errno: %d");
            exit(ListenError);
        }
    }
    int Accept(std::string* ip, uint16_t* port)
    {
        sockaddr_in peer; //远处消息
        socklen_t len = sizeof(peer);
        bzero(&peer, len);

        peer.sin_family = AF_INET;
        int fd = accept(sockfp_, (sockaddr*)&peer, &len);
        if(fd < 0)
        {
            lg(Warning, "AcceptError: %s, errno: %d", strerror(errno), errno);
            return -1;
        }

        char str[64];
        // 第一个传IPV4类型,第二个传sockaddr里的ip成员,第三个传缓冲区用于存放转化后的ip,第四个传缓冲区的大小;
        inet_ntop(AF_INET, &peer.sin_addr, str, sizeof(str));
        *ip = str;
        *port = ntohs(peer.sin_port);

        return fd;
    }

    void Close()
    {
        close(sockfp_);
    }

    bool Connect(const std::string &ip, const uint16_t &port)
    {
        struct sockaddr_in peer;
        memset(&peer, 0, sizeof(peer));
        peer.sin_family = AF_INET;
        peer.sin_port = htons(port);
        inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));

        lg(Info, "Try Connect");
        int n = connect(sockfp_, (struct sockaddr*)&peer, sizeof(peer));
        if(n == -1) 
        {
            std::cerr << "connect to " << ip << ":" << port << " error" << std::endl;
            return false;
        }
        lg(Info, "Connect OK");
        return true;
    }

    int Fd()
    {
        return sockfp_;
    }

private:
    int sockfp_;
};